package edu.northwestern.cbits.purple_robot_manager.logging; import java.security.Permissions; import java.util.HashMap; import java.util.Map; import org.json.JSONArray; import org.json.JSONObject; import android.annotation.SuppressLint; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; import edu.northwestern.cbits.purple_robot_manager.ManagerService; import edu.northwestern.cbits.purple_robot_manager.R; import edu.northwestern.cbits.purple_robot_manager.activities.DiagnosticActivity; import edu.northwestern.cbits.purple_robot_manager.activities.PermissionsActivity; import edu.northwestern.cbits.purple_robot_manager.activities.StartActivity; public class SanityManager { public static final int NOTE_ID = 457284567; private static SanityManager _sharedInstance = null; private Context _context = null; private HashMap<String, String> _errors = new HashMap<>(); private HashMap<String, String> _warnings = new HashMap<>(); private HashMap<String, Runnable> _actions = new HashMap<>(); private int _lastStatus = -1; private String _lastTitle = null; private String _lastMessage = null; public SanityManager(Context context) { this._context = context; AlarmManager alarms = (AlarmManager) this._context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(ManagerService.REFRESH_ERROR_STATE_INTENT); PendingIntent pending = PendingIntent.getService(this._context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); alarms.setInexactRepeating(AlarmManager.RTC, 0, 60000, pending); } public static SanityManager getInstance(Context context) { if (SanityManager._sharedInstance != null) return SanityManager._sharedInstance; if (context != null) SanityManager._sharedInstance = new SanityManager(context.getApplicationContext()); return SanityManager._sharedInstance; } @SuppressWarnings("rawtypes") @SuppressLint("NewApi") public void refreshState() { String packageName = this.getClass().getPackage().getName(); String[] checkClasses = this._context.getResources().getStringArray(R.array.sanity_check_classes); for (String className : checkClasses) { Class checkClass = null; try { checkClass = Class.forName(packageName + "." + className); } catch (ClassNotFoundException e) { try { checkClass = Class.forName(className); } catch (ClassNotFoundException ee) { LogManager.getInstance(this._context).logException(ee); } } if (checkClass != null) { try { SanityCheck check = (SanityCheck) checkClass.newInstance(); check.runCheck(this._context); int error = check.getErrorLevel(); if (error == SanityCheck.ERROR) this.addAlert(SanityCheck.ERROR, check.name(this._context), check.getErrorMessage(), check.getAction(this._context)); else if (error == SanityCheck.WARNING) this.addAlert(SanityCheck.WARNING, check.name(this._context), check.getErrorMessage(), check.getAction(this._context)); else this.clearAlert(check.name(this._context)); } catch (InstantiationException | ClassCastException | IllegalAccessException e) { LogManager.getInstance(this._context).logException(e); } } } SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this._context); if (prefs.getBoolean("config_mute_warnings", false) == false) { this._lastStatus = this.getErrorLevel(); int issueCount = this._errors.size() + this._warnings.size(); PendingIntent contentIntent = PendingIntent.getActivity(this._context, SanityManager.NOTE_ID, new Intent(this._context, StartActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); if (this._lastStatus != SanityCheck.OK) contentIntent = PendingIntent.getActivity(this._context, SanityManager.NOTE_ID, new Intent(this._context, DiagnosticActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); NotificationManager noteManager = (NotificationManager) this._context.getSystemService(Context.NOTIFICATION_SERVICE); NotificationCompat.Builder builder = new NotificationCompat.Builder(this._context); builder = builder.setContentIntent(contentIntent); builder = builder.setContentTitle(this._context.getString(R.string.notify_running_title)); builder.setOngoing(true); builder = builder.setSmallIcon(R.drawable.ic_note_normal); builder = builder.setContentText(this._context.getString(R.string.pr_errors_none_label)); if (this._lastStatus != SanityCheck.OK) { if (issueCount == 1) { builder = builder.setContentText(this._context.getString(R.string.note_purple_robot_message_single)); builder = builder.setTicker(this._context.getString(R.string.note_purple_robot_message_single)); } else { builder = builder.setContentText(this._context.getString(R.string.note_purple_robot_message_multiple, issueCount)); builder = builder.setTicker(this._context.getString(R.string.note_purple_robot_message_multiple, issueCount)); } if (this._lastStatus == SanityCheck.ERROR) builder = builder.setSmallIcon(R.drawable.ic_note_error); else builder = builder.setSmallIcon(R.drawable.ic_note_warning); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); style = style.setBigContentTitle(this._context.getString(R.string.note_purple_robot_status)); if (issueCount == 1) style = style.setSummaryText(this._context.getString(R.string.note_purple_robot_message_single)); else style = style.setSummaryText(this._context.getString(R.string.note_purple_robot_message_multiple, issueCount)); synchronized(this._errors) { for (String key : this._errors.keySet()) style = style.addLine(this._errors.get(key)); } synchronized(this._warnings) { for (String key : this._warnings.keySet()) style = style.addLine(this._warnings.get(key)); } builder = builder.setStyle(style); } } Notification note = builder.build(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) note.color = 0xff4e015c; noteManager.notify(SanityManager.NOTE_ID, note); } } public int getErrorLevel() { if (this._errors.size() > 0) return SanityCheck.ERROR; else if (this._warnings.size() > 0) return SanityCheck.WARNING; return SanityCheck.OK; } public int getErrorIconResource() { switch (this.getErrorLevel()) { case SanityCheck.ERROR: return R.drawable.action_error; case SanityCheck.WARNING: return R.drawable.action_warning; } return R.drawable.action_about; } public void addAlert(int level, String name, String message, Runnable action) { boolean alert = false; if (level == SanityCheck.WARNING && this._warnings.containsKey(name) == false) { synchronized(this._warnings) { this._warnings.put(name, message); alert = true; } } else if (this._warnings.containsKey(name) == false) { synchronized(this._errors) { this._errors.put(name, message); alert = true; } } if (action != null) { synchronized(this._actions) { this._actions.put(name, action); } } if (alert) { if (name.equals(this._lastTitle) && message.equals(this._lastMessage)) { } else { Intent pebbleIntent = new Intent("com.getpebble.action.SEND_NOTIFICATION"); HashMap<String, String> data = new HashMap<>(); data.put("title", name); data.put("body", message); JSONObject jsonData = new JSONObject(data); String notificationData = new JSONArray().put(jsonData).toString(); pebbleIntent.putExtra("messageType", "PEBBLE_ALERT"); pebbleIntent.putExtra("sender", this._context.getString(R.string.app_name)); pebbleIntent.putExtra("notificationData", notificationData); // this._context.sendBroadcast(pebbleIntent); this._lastMessage = message; this._lastTitle = name; } } } public void runActionForAlert(String name) { Runnable r = this._actions.get(name); if (r != null) { Thread t = new Thread(r); t.start(); } } @SuppressWarnings("unchecked") public Map<String, String> errors() { return (Map<String, String>) this._errors.clone(); } @SuppressWarnings("unchecked") public Map<String, String> warnings() { return (Map<String, String>) this._warnings.clone(); } public void clearAlert(String title) { synchronized(this._warnings) { this._warnings.remove(title); } synchronized(this._errors) { this._errors.remove(title); } synchronized (this._actions) { this._actions.remove(title); } } public void addPermissionAlert(String requester, String permission, String rationale, Runnable r) { final SanityManager me = this; if (r == null) { r = new Runnable() { @Override public void run() { Intent intent = new Intent(me._context, PermissionsActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); me._context.startActivity(intent); } }; } String title = requester + ": " + PermissionsActivity.getTitle(this._context, permission); this.addAlert(SanityCheck.ERROR, title, rationale, r); } public void clearPermissionAlert(String permission) { String title = PermissionsActivity.getTitle(this._context, permission); this.clearAlert(title); } }